﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data;


namespace Grishko.DataTier.SqlLibrary
{

    /// <summary>
    /// Базовый класс по загрузке списков - по умолчанию строит предложение позволяющее отфильтровать по указанному 
    /// v_propertyOf
    /// Правила формирования:
    /// На основании vPropertyOfList - первый запрос формирующий выборку из v_propertyOf
    /// Из полученной выборки нолько то что указанно в FilterList
    /// На результат - разобрать лексему - получить предложение , побить на странички - выдать.    
    /// </summary>

    public class ListLoadStatment : SqlStatment
    {

        public ListLoadStatment() { }
        public ListLoadStatment(string FindString) : base(FindString) { }

        protected int _StartRowIndex = 0;
        protected int _EndRowIndex = 100;

        /// <summary>
        /// Paging StartRowIndex
        /// </summary>
        public int StartRowIndex { get { return _StartRowIndex; } set { _StartRowIndex = value; } }
        /// <summary>
        /// Paging EndRowIndex
        /// </summary>
        public int EndRowIndex { get { return _EndRowIndex; } set { _EndRowIndex = value; } }

        /// <summary>
        /// Нименование vPropertyOf (вьюха ресурс свойств)
        /// </summary>
        public virtual string vPropertyOf_ViewName { get { return string.Empty; } }

        /// <summary>
        /// Вьюха ресурс данных
        /// </summary>
        public virtual string Source_ViewName
        {
            get { return string.Empty; }
        }

        /// <summary>
        /// Подсказки параметров в строке поиска
        /// Значение столбца ParamTxt в vPropertyOf
        /// </summary>
        public  virtual List<string> GetHintFindStrings()
        {
                    List<string> res = new List<string>();
                    SqlTools tls = new SqlTools(AppContext.GetSC());
                    DataSet ds = tls.ExecuteSql("select distinct replace(ParamTxt,' ','%') as s from " + vPropertyOf_ViewNameWithRestriction + " order by s");
                    DataTable tbl = ds.Tables[0];
                    foreach (DataRow rw in tbl.Rows )
                    { 
                        res.Add (rw[0].ToString ());
                    }
                    return res;
                    
            
        }

        
        public override string GetStatment()
        {         
            StringBuilder sb = new StringBuilder();
            //sb.Append(" select top 0 ID as ID   into #SelectList from v_Agency \t");
            sb.Append(" DECLARE @SelectList TABLE (ID varchar(36)) \t");
             
            
            if(FilterList.Count >0 || ParamValueList.Count >0 )
                sb.Append(" select top 0 ID as ID   into #Restriction from v_Agency \t");

            if(FilterList.Count >0 )
                sb.Append (DO_GetFilterlist_Sql_1());                   //формировать #FilterList по входному списку (Filterlist)
            
            if(ParamValueList.Count >0) 
                sb.Append (DO_GetPropertyOfList_Sql_2 ());              //формировать #FilterList по входному списку (vPropertyOf_ViewName)


            if(Lexeme .Length >2)
                sb.Append (DO_Lexeme_Sql_3 ());                             //формировать #FilterList по лексеме (Lexeme)


            if(Lexeme.Length <3) // если нет лексемы  ищем по всему массиву
                sb.Append(" if not  exists(select * from @SelectList)  insert into @SelectList  select distinct source.Id from " + vPropertyOf_ViewNameWithRestriction);
                    
            sb.Append (DO_ResultSet_Sql_4 ());                          //фомировать результирующую выборку  
            sb.Append (Do_PageSet_Sql_5());                             //побить результат по страницам         
            sb.Append( Do_PageSetToXml_Sql_6());                        //страницу в xml 

            return sb.ToString();

        }

        /// <summary>
        /// Источник 
        /// </summary>
        protected  string vPropertyOf_ViewNameWithRestriction
        {
            get
            {
                string sRestriction = "";
                if (this.ParamValueList.Count > 0 || FilterList .Count >0)
                    sRestriction = " join #Restriction on  #Restriction.id =Source.id ";

                return vPropertyOf_ViewName + "  as Source " + sRestriction;
            }
        }

        /// <summary>
        /// Формировать по списку FilterList (#FilterList )
        /// </summary>
        /// <returns></returns>
        protected virtual string DO_GetFilterlist_Sql_1() 
        {
            StringBuilder sql = new StringBuilder();
            if (this.FilterList.Count == 0)
                return sql.ToString ();

                 sql.Append ("declare @FilterList_xml as xml set @FilterList_xml=' " + FilterList.ToXml(true) + "'");
                 sql.Append(" INSERT INTO #Restriction  SELECT  T.c.value('(@ID)[1]','varchar(36)') as ID   FROM    @FilterList_xml.nodes('/EntityList/EntitySingle') T(c)");
            return sql.ToString();

            
        }
        /// <summary>
        /// Формировать ограничения по списку Параметр/Значение (#FilterList )
        /// </summary>
        /// <returns></returns>
        protected virtual string DO_GetPropertyOfList_Sql_2()
        {
            if (this.ParamValueList.Count == 0)
                return string.Empty;

            StringBuilder sql = new StringBuilder();            
            sql.Append(_ParamValueListBox.GetSqlForTable("#Restriction"));            
            return sql.ToString ();
        }

        /// <summary>
        /// Формировать ограничение на основании лексемы (#FilterList )
        /// </summary>
        /// <returns></returns>
        protected virtual string DO_Lexeme_Sql_3()
        {
            //0. создаем 
           // 1. расчитываем Like
           // 2. DoNot Like
           // 3. Не параметризированные перечисления параметр,параметр,параметр

            StringBuilder sql= new StringBuilder ();
             
            List<SqlWord> LikeWords = GetWords(SqlWord.SqlWordType.Like);
            string LikeWhere= string.Empty ;

            // формируем #SelectList            
            if (LikeWords.Count > 0)
            {
                LikeWhere = GetStatment_Where(LikeWords);
                sql.Append("insert into @SelectList " + GetLikeSql(LikeWhere, LikeWords.Count));
            }

            //Слова неформализованного  поиск  все что не относится к перечислению параметр, параметр
            List<SqlWord> NotParamLikeWords = GetWords(SqlWord.SqlWordType.NotParamLike);
            if (NotParamLikeWords.Count > 0)
            {
                LikeWhere = GetStatment_Where(NotParamLikeWords);

                string s= " DECLARE @NotParamSrch TABLE (ID varchar(36)) " +
                    "insert into @NotParamSrch " +
                    " select distinct id from " +
                    vPropertyOf_ViewNameWithRestriction + 
                    " where " + LikeWhere +
                    "if exists(select * from @SelectList)  " +
                    "delete from @SelectList  where id not in  (select id from @NotParamSrch)" +
                    "else " +
                    "insert into @SelectList    select id from @NotParamSrch ";

                sql.Append(s);

            }

            
            // Удаляем из результата DoNot
            List<SqlWord> DoNotLikeWords = GetWords(SqlWord.SqlWordType.DoNotLike);
            
            if (DoNotLikeWords.Count > 0)// формируем #SelectList             
            {
                string DoNotLikeWhere = GetStatment_Where(DoNotLikeWords);
                sql.Append(" if not  exists(select * from @SelectList)  insert into @SelectList  select distinct Id from " + vPropertyOf_ViewNameWithRestriction);
                sql.Append(" delete from  @SelectList where id in ( " + GetLikeSql(DoNotLikeWhere, DoNotLikeWords.Count) + ")");                    
            }
            
            

            
            return sql.ToString ();
        }

 
 

        private string GetLikeSql( string Where, int WhereCount)
        {
            

            return 
                "  select Id from" +
                "(" +
                "select " + "Source.ID as ID," +
                 "1 as ItemCount " +
                "from " + vPropertyOf_ViewNameWithRestriction +
                " where 	" +
                Where +
                 ") as res   " +
                "group by res.ID,ItemCount " +
                "having sum(ItemCount)= " + WhereCount.ToString();

        }

        /// <summary>
        /// Формирование результата (#SelectList)
        /// </summary>
        /// <returns></returns>
        protected virtual string DO_ResultSet_Sql_4()
        {
            StringBuilder sql = new StringBuilder();

            sql.Append(" declare @r_count int select Src.ID,[XML] into 	#ResultSet from " + Source_ViewName + " as Src join @SelectList as SelectList on  Src.ID=SelectList.Id DECLARE @OutputRwCnt  INT select @OutputRwCnt=count(*) from #ResultSet ");
            return sql.ToString();
        }

        /// <summary>
        /// Пэйджинг результата 
        /// </summary>
        /// <returns></returns>
        protected virtual string Do_PageSet_Sql_5()
        {
            return " select 	[XML] into 	#Page from ( select ID, [XML] , ROW_NUMBER() OVER (ORDER BY ID) AS row_num  from #ResultSet) as Page where row_num between " + _StartRowIndex.ToString() + " and " + _EndRowIndex.ToString();
        }
        
        protected virtual string Do_PageSetToXml_Sql_6()
        {
            return
             " declare @r as xml select @r=(select [xml].query('.') from 	#Page FOR XML PATH(''), TYPE, ELEMENTS , ROOT('EntityList') )" +
              " if not(@r is null)   SET @r.modify('insert attribute OutputRwCnt {sql:variable(''@OutputRwCnt'')} into (/EntityList)[1]')" +
              " select @r";
        }

        #region old
        //protected virtual string GetPropertyOfSql()
        //{
            
        //    //string sql = string.Empty;

        //    ////1) Сформировать #FilterList - набор итоговых ид из переменной xml
        //    //FilterList.Reset();
        //    //if (FilterList.Count > 0) //  есть четко обозначеный список ограничений - _FilterList>0 делаем срез по нему 
        //    //{
        //    //    //#FilterList
        //    //    sql = sql + "declare @FilterList_xml as xml set @FilterList_xml=' " + FilterList.ToXml(true) + "'";
        //    //    sql = sql + " SELECT  T.c.value('(@ID)[1]','varchar(36)') as ID INTO #FilterList FROM    @FilterList_xml.nodes('/EntityList/EntitySingle') T(c)";
        //    //}

        //    ////2) Наложить ограничения по наборам параметр|значение (если был шаг 1 то на его результат - нет на vPropertyOf_ViewName)
        //    //if (ParamValueList.Count > 0) // есть ограничения по парам параметр-значение 
        //    //{
        //    //    //##ParamValueList
        //    //    sql = sql + _ParamValueListBox.GetSqlForTable("#ParamValueList");// получаем второй срез на основании vPropertyOfList (запрос по парам парметр,значение)

        //    //}

        //    ////3) Сливаем списки ограничения если были
        //    //bool bHaveInLists = false; // есть ограничения внешним списком или набором параметов ?
        //    //bHaveInLists = (FilterList.Count + ParamValueList.Count) > 0;

        //    //if (bHaveInLists)
        //    //    sql = sql + "select top 0 id into #SourcePropOf  from " + Source_ViewName;

        //    //if (FilterList.Count > 0)
        //    //    sql = sql + " insert into #SourcePropOf select id from #FilterList ";

        //    //if (ParamValueList.Count > 0)
        //    //{
        //    //    sql = sql + " insert into #SourcePropOf select #ParamValueList.id from #ParamValueList ";
        //    //    if (FilterList.Count > 0) sql = sql + " join #FilterList on #FilterList.id=#ParamValueList.id ";
        //    //}

        //    //string _Source_ViewName = " #SourcePropOf";
        //    //if (FilterList.Count == 0 && ParamValueList.Count == 0)
        //    //    _Source_ViewName = Source_ViewName;


        //    //if (where.Length == 0)  //нет лексемы?                  
        //    //{
        //    //    return sql + " select distinct Id into #SelectList  from  " + (bHaveInLists ? _Source_ViewName : Source_ViewName);
        //    //}

        //    /////есть лексема - добираем запрос согласно лексеме           

        //    ////a) Если были внеш ограничения - режем vPropertyOf_ViewName
        //    //if (bHaveInLists)
        //    //{
        //    //    sql = sql + " select " + vPropertyOf_ViewName + ".* into #vPropertyOf_ViewName from " + vPropertyOf_ViewName + " join  #SourcePropOf on " + vPropertyOf_ViewName + ".id = #SourcePropOf.id ";

        //    //}

        //    //sql = sql + " select Id into #SelectList  from" +
        //    // "(" +
        //    // "select " + (bHaveInLists ? "#vPropertyOf_ViewName" : vPropertyOf_ViewName) + ".ID as ID," +
        //    //     "1 as ItemCount " +
        //    // "from " + (bHaveInLists ? "#vPropertyOf_ViewName" : vPropertyOf_ViewName) +
        //    // " where 	" +
        //    //  where +
        //    //     ") as res   " +
        //    // "group by res.ID,ItemCount " +
        //    // "having sum(ItemCount)= " + this.ChildNodes.Count.ToString();
        //    //return sql;
        //    return string.Empty; 
        //}

        ///// <summary>
        ///// Формирование условия where - отрицания значений
        ///// </summary>
        ///// <returns></returns>
        //protected virtual string GetNotPropertyOfString()
        //{
        //    return string.Empty;
        //    //string w = GetNotStatment();
        //    //if (w.Length == 0) return string.Empty;
        //    //return " delete from #SelectList where id in (select id from " + vPropertyOf_ViewName + " where " + w + ") ";
        //}

        ///// <summary>
        ///// скрипт по вычислению реально отобранных величин 
        ///// </summary>
        ///// <returns></returns>
        //protected virtual string GetResultSetSql()
        //{
        //    return " declare @r_count int select " + Source_ViewName + ".ID,[XML] into 	#ResultSet from " + Source_ViewName + " join #SelectList on " + Source_ViewName + ".ID=#SelectList.Id DECLARE @OutputRwCnt  INT select @OutputRwCnt=count(*) from #ResultSet ";
        //}

        ///// <summary>
        ///// Формирование странички 
        ///// </summary>
        ///// <returns></returns>
        //protected virtual string GetPageSql()
        //{
        //    return " select 	[XML] into 	#Page from ( select ID, [XML] , ROW_NUMBER() OVER (ORDER BY ID) AS row_num  from #ResultSet) as Page where row_num between " + _StartRowIndex.ToString() + " and " + _EndRowIndex.ToString();
        //}

        ///// <summary>
        ///// Преобразование результата в xml переменную 
        ///// </summary>
        ///// <returns></returns>
        //protected virtual string GetResultToXmlVarible()
        //{
        //    return
        //   " declare @r as xml select @r=(select [xml].query('.') from 	#Page FOR XML PATH(''), TYPE, ELEMENTS , ROOT('EntityList') )" +
        //    " if not(@r is null)   SET @r.modify('insert attribute OutputRwCnt {sql:variable(''@OutputRwCnt'')} into (/EntityList)[1]')" +
        //    " select @r";
        //}
        #endregion

        private EntityList _FilterList = new EntityList();
        /// <summary>
        /// Набор ID = Накладывающий прямое ограничение
        /// </summary>
        public EntityList FilterList
        {
            get
            {
                return _FilterList;
            }
            set { _FilterList = value; }
        }


        protected vPropertyOfList _ParamValueListBox = new vPropertyOfList();

        /// <summary>
        /// Набор пар = параметр значение, сформировать запрос к 
        /// Коллекция - vPropertyOfList.ParamListItem        
        /// </summary>       
        public EntityList ParamValueList
        {

            get { _ParamValueListBox.ViewName = vPropertyOf_ViewName; return _ParamValueListBox.ParamList; }
        }

        /// <summary>
        /// Опция - указывающя как формирвать запрос по коллекции ParamValueList
        /// парметр/значение, параметр, значение
        /// </summary>
        public vPropertyOfList.Options ParamValueList_SearchOption
        {
            get { return _ParamValueListBox.SqlSearchOption; }
            set { _ParamValueListBox.SqlSearchOption = value; }
        }

    }
}
